package com.kexio.websocket.handler;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.kexio.websocket.dto.WebSocketMessage;
import com.kexio.websocket.service.WebSocketSessionService;

/**
 * WebSocket 处理器
 * 
 * 处理 WebSocket 连接、消息收发等核心逻辑。
 * 
 * @author Kexio Team
 */
@Component
public class WebSocketHandler implements org.springframework.web.socket.WebSocketHandler {

    private static final Logger log = LoggerFactory.getLogger(WebSocketHandler.class);

    @Autowired
    private WebSocketSessionService sessionService;
    
    @Autowired
    @Qualifier("webSocketObjectMapper")
    private ObjectMapper objectMapper;

    // 存储活跃的 WebSocket 会话
    private final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String sessionId = session.getId();
        String userId = getUserIdFromSession(session);
        String tenantId = getTenantIdFromSession(session);
        
        log.info("WebSocket 连接建立: sessionId={}, userId={}, tenantId={}", sessionId, userId, tenantId);
        
        // 存储会话
        sessions.put(sessionId, session);
        
        // 注册用户会话
        if (userId != null) {
            sessionService.registerUserSession(tenantId, userId, sessionId, session);
            
            // 发送连接成功消息
            WebSocketMessage welcomeMessage = WebSocketMessage.systemMessage("连接成功");
            sendMessage(session, welcomeMessage);
        }
    }

    @Override
    public void handleMessage(WebSocketSession session, org.springframework.web.socket.WebSocketMessage<?> message) throws Exception {
        String sessionId = session.getId();
        String userId = getUserIdFromSession(session);
        String tenantId = getTenantIdFromSession(session);
        
        log.debug("收到 WebSocket 消息: sessionId={}, userId={}, messageType={}", sessionId, userId, message.getClass().getSimpleName());
        
        try {
            // 解析消息
            if (message instanceof TextMessage) {
                String payload = ((TextMessage) message).getPayload();
                WebSocketMessage<?> wsMessage = objectMapper.readValue(payload, WebSocketMessage.class);
                
                // 处理不同类型的消息
                handleWebSocketMessage(tenantId, userId, sessionId, wsMessage);
            } else if (message instanceof BinaryMessage) {
                log.warn("暂不支持二进制消息: sessionId={}", sessionId);
            } else if (message instanceof PongMessage) {
                log.debug("收到 pong 消息: sessionId={}", sessionId);
            } else {
                log.warn("未知的消息类型: sessionId={}, messageType={}", sessionId, message.getClass().getSimpleName());
            }
        } catch (Exception e) {
            log.error("处理 WebSocket 消息失败: sessionId={}, error={}", sessionId, e.getMessage(), e);
            
            // 发送错误消息
            WebSocketMessage errorMessage = WebSocketMessage.errorMessage("消息处理失败: " + e.getMessage());
            sendMessage(session, errorMessage);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        String sessionId = session.getId();
        String userId = getUserIdFromSession(session);
        
        log.error("WebSocket 传输错误: sessionId={}, userId={}, error={}", sessionId, userId, exception.getMessage(), exception);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        String sessionId = session.getId();
        String userId = getUserIdFromSession(session);
        String tenantId = getTenantIdFromSession(session);
        
        log.info("WebSocket 连接关闭: sessionId={}, userId={}, status={}", sessionId, userId, closeStatus);
        
        // 移除会话
        sessions.remove(sessionId);
        
        // 注销用户会话
        if (userId != null) {
            sessionService.unregisterUserSession(tenantId, userId, sessionId);
        }
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 处理具体的 WebSocket 消息
     */
    private void handleWebSocketMessage(String tenantId, String userId, String sessionId, WebSocketMessage<?> message) {
        String messageType = message.getType();
        
        switch (messageType) {
            case "ping":
                // 心跳检测
                handlePingMessage(sessionId);
                break;
            case "notification_read":
                // 通知已读
                handleNotificationRead(tenantId, userId, message);
                break;
            case "typing":
                // 正在输入状态
                handleTypingStatus(tenantId, userId, message);
                break;
            default:
                log.warn("未知的消息类型: type={}, sessionId={}", messageType, sessionId);
        }
    }

    /**
     * 处理心跳消息
     */
    private void handlePingMessage(String sessionId) {
        WebSocketSession session = sessions.get(sessionId);
        if (session != null && session.isOpen()) {
            WebSocketMessage pongMessage = WebSocketMessage.pongMessage();
            sendMessage(session, pongMessage);
        }
    }

    /**
     * 处理通知已读消息
     */
    private void handleNotificationRead(String tenantId, String userId, WebSocketMessage<?> message) {
        // 这里可以触发通知已读的业务逻辑
        log.debug("处理通知已读: tenantId={}, userId={}, data={}", tenantId, userId, message.getData());
    }

    /**
     * 处理正在输入状态
     */
    private void handleTypingStatus(String tenantId, String userId, WebSocketMessage<?> message) {
        // 广播正在输入状态给相关用户
        log.debug("处理正在输入状态: tenantId={}, userId={}, data={}", tenantId, userId, message.getData());
    }

    /**
     * 发送消息到指定会话
     */
    public void sendMessage(WebSocketSession session, WebSocketMessage message) {
        if (session != null && session.isOpen()) {
            try {
                String payload = objectMapper.writeValueAsString(message);
                session.sendMessage(new TextMessage(payload));
            } catch (IOException e) {
                log.error("发送 WebSocket 消息失败: sessionId={}, error={}", session.getId(), e.getMessage(), e);
            }
        }
    }

    /**
     * 发送消息给指定用户的所有会话
     */
    public void sendMessageToUser(String tenantId, String userId, WebSocketMessage message) {
        sessionService.getUserSessions(tenantId, userId).forEach(session -> {
            sendMessage(session, message);
        });
    }

    /**
     * 广播消息给所有在线用户
     */
    public void broadcastMessage(String tenantId, WebSocketMessage message) {
        sessionService.getAllUserSessions(tenantId).forEach(session -> {
            sendMessage(session, message);
        });
    }

    /**
     * 从会话中获取用户ID
     */
    private String getUserIdFromSession(WebSocketSession session) {
        Object userId = session.getAttributes().get("userId");
        return userId != null ? userId.toString() : null;
    }

    /**
     * 从会话中获取租户ID
     */
    private String getTenantIdFromSession(WebSocketSession session) {
        Object tenantId = session.getAttributes().get("tenantId");
        return tenantId != null ? tenantId.toString() : null;
    }
}
